#ifndef HCNG_H
#define HCNG_H

#include <QObject>
#include "QSerialPort"
#include "frame_driver.h"
#include "QTimer"
#include <QFileDialog>
#include <QTextStream>

class HCNG:public QObject
{
    Q_OBJECT
public:
//帧格式定义:帧标识符 帧标识符取反 数据长度 数据(用户数据+CRC16校验)
//帧用户数据格式定义:发送设备地址 接收设备地址 帧命令类型 命令编码 其它帧信息
#define dFrame_IdentifyA 0x81//帧标识符
#define dFrame_IdentifyA_Invert 0x7E//帧标识符取反
#define dFrame_LengthMax 512//帧最大长度
#define dDataLength_MSB(len) (((len)>>8)&0xFF)//截取数据长度高8位
#define dDataLength_LSB(len) ((len)&0xFF)//截取数据长度低8位
#define dFilemode 0
    //协议格式:帧头 帧长度 数据 校验位
    //数据格式:发送设备地址 接收设备地址 命令类型 命令编码 具体命令内容
    typedef struct
    {
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
        unsigned char byte4;
    }Byte4_MemoryParameterStructDef;
    typedef union
    {
        float Value;
        Byte4_MemoryParameterStructDef Memory;
    }Convert_FloatParameter_UnionDef;

    typedef struct
    {
        unsigned char byte1;
        unsigned char byte2;
    }Byte2_MemoryParameterStructDef;
    typedef union
    {
        quint16 Value;
        Byte2_MemoryParameterStructDef Memory;
    }Convert_Quint16Parameter_UnionDef;
    typedef enum
    {
        eDeviceAddress_Broadcast = 0xFF,//广播地址
        eDeviceAddress_Master = 0xFE,//主设备地址
        eDeviceAddress_Slave = 0x00,//其余地址都是从设备地址
        eDeviceAddress_Num
    }Protocol_DeviceAddress_EnumDef;//设备地址

    typedef enum
    {
        eCommandType_Error = 0,//错误
        eCommandType_Device,//设备配置信息
        eCommandType_Debug,//调试
        eCommandType_Num
    }Protocol_CommandType_EnumDef;//帧命令类型
    typedef enum
    {
        eCodeType_Error_Checksum = 0,//校验错误
        eCodeType_Error_Parameter,//参数错误
        eCodeType_Error_DeviceFault,//设备故障
        eCodeType_Error_Num
    }Protocol_CodeType_Error_EnumDef;//错误命令编码值
    typedef enum
    {
        eMemory_FileObject_SystemConfiguration = 0,//系统配置文件
        eMemory_FileObject_User0,//用户0数据文件;行数据格式见FileFormatContent_User_StructDef
        eMemory_FileObject_VoltageTemperature,//温度传感器电压对应温度校准数据文件;行数据格式见FileFormatContent_CalibrationData_StructDef
        eMemory_FileObject_VoltageHydrogen,//氢气传感器电压对应氢气浓度校准数据文件;行数据格式见FileFormatContent_CalibrationData_StructDef
        eMemory_FileObject_Num
    }Memory_FileObject_EnumDef;
    typedef enum
    {
        eCodeType_Device_SystemReset_Request = 0,//系统重启;内容=空;此命令后请等待3秒后再使用
        eCodeType_Device_SystemReset_Ack,//内容=空
        eCodeType_Device_SystemRestore_Request,//设备恢复出厂设置;内容=空;此命令后请等待3秒后再使用
        eCodeType_Device_SystemRestore_Ack,//内容=空
        eCodeType_Device_Address_Set,//设备地址设置:内容=1字节=新地址值,需符合设备地址规定,见Protocol_DeviceAddress_EnumDef
        eCodeType_Device_Address_Ack,//内容=空,此命令以旧地址发送,此后需要新地址访问;此命令后等待200ms再使用
        eCodeType_Device_Sample_Query,//采样传感器数据;内容=空
        eCodeType_Device_Sample_Ack,//内容=16字节=采样数据;温度+浓度+温度检测电压+氢气检测电压,参数均float(4个字节)
        eCodeType_Device_CalibrationDataUse_Set,//系统运行时校准数据使用|不使用;内容=1字节=(假:不使用;真:使用)
        eCodeType_Device_CalibrationDataUse_Ack,//内容=空
        eCodeType_Device_CalibrationDataLoad_Request,//加载校准数据;内容=空;此命令可以不使用,User0文件写入完毕会自动加载校准数据
        eCodeType_Device_CalibrationDataLoad_Ack,//内容=空
        eCodeType_Device_CalibrationDataDelete_Request,//校准数据删除;内容=选择文件对象(见Memory_FileObject_EnumDef)
        eCodeType_Device_CalibrationDataDelete_Ack,//内容=空
        eCodeType_Device_FilePrintf_Request,//文件查阅(串口打印);内容=选择文件对象(见Memory_FileObject_EnumDef);此命令调试使用,可忽略
        eCodeType_Device_FilePrintf_Ack,//内容=空
        eCodeType_Device_FileTransmit_Request,//文件传输请求:内容=选择文件对象(见Memory_FileObject_EnumDef)
        eCodeType_Device_FileTransmit_Data,//文件传输数据:内容=具体文件行数据格式
        eCodeType_Device_FileTransmit_DataAck,//文件传输应答:内容=异常值(0-正常,1-参数错误,2-文件数据空,3-文件操作失败,其它值-保留)
        eCodeType_Device_FileTransmit_Finish,//文件传输结束:内容=文件行数;eMemory_FileObject_User0文件写入完毕会自动加载校准数据,并使能校准计算
        eCodeType_Device_FileTransmit_FinishAck,//文件传输应答:内容=异常值(0-正常,1-参数错误,2-文件数据空,3-文件操作失败,其它值-保留)
        /************************************************
  上位机读取文件流程：FileTransmit
      上位机                      下位机
      Request-》
                                《-Data
      ...
                                《-Data
                                《-Finish
  上位机写入文件流程：FileTransmit
      下位机                      上位机
                                《-Data
      DataAck-》
      ...
                                《-Data
      DataAck-》
                                《-Finish
      FinishAck-》//上位机必须等待此Ack后才能操作，因为下位机会在Finish之后计算拟合数据并保存至flash，存储结束之后才会应答Ack
  ************************************************/
        eCodeType_Device_OperationInformation_Set,//使能|禁止打印运行信息;内容=1字节=(假:不使用;真:使用);调试使用,可忽略
        eCodeType_Device_OperationInformation_Ack,//内容=空
        eCodeType_Device_Num
    }Protocol_CodeType_Device_EnumDef;//设备操作命令编码值

    typedef enum
    {
        eCodeType_Debug_Developer = 0,//开发者调试命令
        eCodeType_Debug_Num
    }Protocol_CodeType_Debug_EnumDef;//调试命令编码值
    typedef struct
    {
        uint16_t SerialNumber;//序号;0开始
        float temperature;//温度,单位℃
        float concentration;//浓度
        float VoltageTemperature;//温度检测电压,单位V
        float VoltageHydrogen;//氢气检测电压,单位V
    }FileFormatContent_User_StructDef;//用户文件内容格式
    HCNG();
    ~HCNG();

    uint8_t  RunFileTransmit_slot();
    bool FrameCommand_ValueCheck(quint8 address,quint8 cmd,quint8 code);
    bool FrameCommandSend_General(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code);
    bool FrameCommandSend_General_OneValue(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,uint8_t value);
    bool FrameCommandSend_General_OneState(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,bool State);
    bool FrameCommandSend_General_FileObject(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,Memory_FileObject_EnumDef Object);
    bool FrameCommandSend_General_FileFormat(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,FileFormatContent_User_StructDef FileFormat);
    bool Protocol_CommandSend_FileTransmit_Finish(quint8 address,quint8 *buf,quint8 object,quint16 LineNumber);
    bool Protocol_CommandSend_FileTransmit_DataUser0(quint8 address,quint8 *buf,quint32 BufferSize,quint16 &index,quint16 &ResidueNumber,FileFormatContent_User_StructDef *data);
    bool FrameCommandSend_SystemRestore(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code);


signals:
    void MessageHint_signal(QString message);//运行信息输出
    void Serial_Connected_signal(QString baudRate,QString port);
    void Serial_DisConnected_signal(void);
    void Serial_TransferData_signal(quint8 *data,quint32 length);
    void FrameCommandReceive_signal(quint8 FrameIdentify,quint8 *data,quint32 length);


private:

    typedef struct
    {
        QSerialPort *serialPort;

        //bool socket_state;//客户端连接状态:假-未连接，真-连接成功
        frame_handler frame;//协议帧句柄
        uint8_t FrameSendBufferA[1024+4];//注意：此大小必须等于dFrame_LengthMax，修改帧大小后必须手动修改此值
        QTimer timer;

        quint64 TxNum;
        quint64 RxNum;
    }ParameterHandler_StructDef;//参数句柄
    ParameterHandler_StructDef parameter;

    void Protocol_Find(quint8 *data,quint32 length);
    void frame_data_qDebug(Frame_Data_StructDef data);

private slots:
    void Serial_Connected_slot(QString baudRate,QString port);
    void Serial_DisConnected_slot(void);
    void Serial_Error_slot(QSerialPort::SerialPortError error);
    void Serial_TransferData_slot(quint8 *data,quint32 length);
    void Serial_ReadyRead_slot(void);
    void Timer_slot(void);

};

#endif // HCNG_H
